(function(b) {
  var a = {};
  a.config = {
    editable: true,
    lineHeight: 15,
    basePath: "",
    rect: {
      attr: {
        x: 10,
        y: 10,
        width: 100,
        height: 50,
        r: 5,
        fill: "90-#fff-#C0C0C0",
        stroke: "#000",
        "stroke-width": 1
      },
      showType: "image&text",
      type: "state",
      name: {
        text: "state",
        "font-style": "italic"
      },
      text: {
        text: "状态",
        "font-size": 13
      },
      margin: 5,
      props: [],
      img: {}
    },
    path: {
      attr: {
        path: {
          path: "M10 10L100 100",
          stroke: "#808080",
          fill: "none",
          "stroke-width": 2
        },
        arrow: {
          path: "M10 10L10 10",
          stroke: "#808080",
          fill: "#808080",
          "stroke-width": 2,
          radius: 4
        },
        fromDot: {
          width: 5,
          height: 5,
          stroke: "#fff",
          fill: "#000",
          cursor: "move",
          "stroke-width": 2
        },
        toDot: {
          width: 5,
          height: 5,
          stroke: "#fff",
          fill: "#000",
          cursor: "move",
          "stroke-width": 2
        },
        bigDot: {
          width: 5,
          height: 5,
          stroke: "#fff",
          fill: "#000",
          cursor: "move",
          "stroke-width": 2
        },
        smallDot: {
          width: 5,
          height: 5,
          stroke: "#fff",
          fill: "#000",
          cursor: "move",
          "stroke-width": 3
        }
      },
      text: {
        text: "TO {to}",
        cursor: "move",
        background: "#000"
      },
      textPos: {
        x: 0,
        y: -10
      },
      props: {
        text: {
          name: "text",
          label: "显示",
          value: "",
          editor: function() {
            return new a.editors.textEditor()
          }
        }
      }
    },
    tools: {
      attr: {
        left: 10,
        top: 10
      },
      pointer: {},
      path: {},
      states: {},
      save: {
        onclick: function(c) {
          alert(c)
        }
      }
    },
    props: {
      attr: {
        top: 10,
        right: 30
      },
      props: {}
    },
    restore: "",
    flowName:"",
    activeRects: {
      rects: [],
      rectAttr: {
        stroke: "#ff0000",
        "stroke-width": 2
      }
    },
    historyRects: {
      rects: [],
      pathAttr: {
        path: {
          stroke: "#00ff00"
        },
        arrow: {
          stroke: "#00ff00",
          fill: "#00ff00"
        }
      }
    }
  };
  a.util = {
    isLine: function(g, f, e) {
      var d, c;
      if ((g.x - e.x) == 0) {
        d = 1
      } else {
        d = (g.y - e.y) / (g.x - e.x)
      }
      c = (f.x - e.x) * d + e.y;
      if ((f.y - c) < 10 && (f.y - c) > -10) {
        f.y = c;
        return true
      }
      return false
    },
    center: function(d, c) {
      return {
        x: (d.x - c.x) / 2 + c.x,
        y: (d.y - c.y) / 2 + c.y
      }
    },
    nextId: (function() {
      var c = 0;
      return function() {
        return++c
      }
    })(),
    connPoint: function(j, d) {
      var c = d,
      e = {
        x: j.x + j.width / 2,
        y: j.y + j.height / 2
      };
      var l = (e.y - c.y) / (e.x - c.x);
      l = isNaN(l) ? 0 : l;
      var k = j.height / j.width;
      var h = c.y < e.y ? -1 : 1,
      f = c.x < e.x ? -1 : 1,
      g,
      i;
      if (Math.abs(l) > k && h == -1) {
        g = e.y - j.height / 2;
        i = e.x + h * j.height / 2 / l
      } else {
        if (Math.abs(l) > k && h == 1) {
          g = e.y + j.height / 2;
          i = e.x + h * j.height / 2 / l
        } else {
          if (Math.abs(l) < k && f == -1) {
            g = e.y + f * j.width / 2 * l;
            i = e.x - j.width / 2
          } else {
            if (Math.abs(l) < k && f == 1) {
              g = e.y + j.width / 2 * l;
              i = e.x + j.width / 2
            }
          }
        }
      }
      return {
        x: i,
        y: g
      }
    },
    arrow: function(l, k, d) {
      var g = Math.atan2(l.y - k.y, k.x - l.x) * (180 / Math.PI);
      var h = k.x - d * Math.cos(g * (Math.PI / 180));
      var f = k.y + d * Math.sin(g * (Math.PI / 180));
      var e = h + d * Math.cos((g + 120) * (Math.PI / 180));
      var j = f - d * Math.sin((g + 120) * (Math.PI / 180));
      var c = h + d * Math.cos((g + 240) * (Math.PI / 180));
      var i = f - d * Math.sin((g + 240) * (Math.PI / 180));
      return [k, {
        x: e,
        y: j
      },
      {
        x: c,
        y: i
      }]
    }
  };
  a.rect = function(p, m) {
    var u = this,
    g = "rect" + a.util.nextId(),
    E = b.extend(true, {},
    a.config.rect, p),
    C = m,
    t,
    e,
    n,
    f,
    x,
    v;
    t = C.rect(E.attr.x, E.attr.y, E.attr.width, E.attr.height, E.attr.r).hide().attr(E.attr);
    e = C.image(a.config.basePath + E.img.src, E.attr.x + E.img.width / 2, E.attr.y + (E.attr.height - E.img.height) / 2, E.img.width, E.img.height).hide();
    n = C.text(E.attr.x + E.img.width + (E.attr.width - E.img.width) / 2, E.attr.y + a.config.lineHeight / 2, E.name.text).hide().attr(E.name);
    f = C.text(E.attr.x + E.img.width + (E.attr.width - E.img.width) / 2, E.attr.y + (E.attr.height - a.config.lineHeight) / 2 + a.config.lineHeight, E.text.text).hide().attr(E.text);
    t.drag(function(r, o) {
      A(r, o)
    },
    function() {
      z()
    },
    function() {
      l()
    });
    e.drag(function(r, o) {
      A(r, o)
    },
    function() {
      z()
    },
    function() {
      l()
    });
    n.drag(function(r, o) {
      A(r, o)
    },
    function() {
      z()
    },
    function() {
      l()
    });
    f.drag(function(r, o) {
      A(r, o)
    },
    function() {
      z()
    },
    function() {
      l()
    });
    var A = function(F, r) {
      if (!a.config.editable) {
        return
      }
      var o = (x + F);
      var G = (v + r);
      q.x = o - E.margin;
      q.y = G - E.margin;
      B()
    };
    var z = function() {
      x = t.attr("x");
      v = t.attr("y");
      t.attr({
        opacity: 0.5
      });
      e.attr({
        opacity: 0.5
      });
      f.attr({
        opacity: 0.5
      })
    };
    var l = function() {
      t.attr({
        opacity: 1
      });
      e.attr({
        opacity: 1
      });
      f.attr({
        opacity: 1
      })
    };
    var s, i = {},
    h = 5,
    q = {
      x: E.attr.x - E.margin,
      y: E.attr.y - E.margin,
      width: E.attr.width + E.margin * 2,
      height: E.attr.height + E.margin * 2
    };
    s = C.path("M0 0L1 1").hide();
    i.t = C.rect(0, 0, h, h).attr({
      fill: "#000",
      stroke: "#fff",
      cursor: "s-resize"
    }).hide().drag(function(r, o) {
      D(r, o, "t")
    },
    function() {
      k(this.attr("x") + h / 2, this.attr("y") + h / 2, "t")
    },
    function() {});
    i.lt = C.rect(0, 0, h, h).attr({
      fill: "#000",
      stroke: "#fff",
      cursor: "nw-resize"
    }).hide().drag(function(r, o) {
      D(r, o, "lt")
    },
    function() {
      k(this.attr("x") + h / 2, this.attr("y") + h / 2, "lt")
    },
    function() {});
    i.l = C.rect(0, 0, h, h).attr({
      fill: "#000",
      stroke: "#fff",
      cursor: "w-resize"
    }).hide().drag(function(r, o) {
      D(r, o, "l")
    },
    function() {
      k(this.attr("x") + h / 2, this.attr("y") + h / 2, "l")
    },
    function() {});
    i.lb = C.rect(0, 0, h, h).attr({
      fill: "#000",
      stroke: "#fff",
      cursor: "sw-resize"
    }).hide().drag(function(r, o) {
      D(r, o, "lb")
    },
    function() {
      k(this.attr("x") + h / 2, this.attr("y") + h / 2, "lb")
    },
    function() {});
    i.b = C.rect(0, 0, h, h).attr({
      fill: "#000",
      stroke: "#fff",
      cursor: "s-resize"
    }).hide().drag(function(r, o) {
      D(r, o, "b")
    },
    function() {
      k(this.attr("x") + h / 2, this.attr("y") + h / 2, "b")
    },
    function() {});
    i.rb = C.rect(0, 0, h, h).attr({
      fill: "#000",
      stroke: "#fff",
      cursor: "se-resize"
    }).hide().drag(function(r, o) {
      D(r, o, "rb")
    },
    function() {
      k(this.attr("x") + h / 2, this.attr("y") + h / 2, "rb")
    },
    function() {});
    i.r = C.rect(0, 0, h, h).attr({
      fill: "#000",
      stroke: "#fff",
      cursor: "w-resize"
    }).hide().drag(function(r, o) {
      D(r, o, "r")
    },
    function() {
      k(this.attr("x") + h / 2, this.attr("y") + h / 2, "r")
    },
    function() {});
    i.rt = C.rect(0, 0, h, h).attr({
      fill: "#000",
      stroke: "#fff",
      cursor: "ne-resize"
    }).hide().drag(function(r, o) {
      D(r, o, "rt")
    },
    function() {
      k(this.attr("x") + h / 2, this.attr("y") + h / 2, "rt")
    },
    function() {});
    var D = function(F, r, G) {
      if (!a.config.editable) {
        return
      }
      var o = _bx + F,
      H = _by + r;
      switch (G) {
      case "t":
        q.height += q.y - H;
        q.y = H;
        break;
      case "lt":
        q.width += q.x - o;
        q.height += q.y - H;
        q.x = o;
        q.y = H;
        break;
      case "l":
        q.width += q.x - o;
        q.x = o;
        break;
      case "lb":
        q.height = H - q.y;
        q.width += q.x - o;
        q.x = o;
        break;
      case "b":
        q.height = H - q.y;
        break;
      case "rb":
        q.height = H - q.y;
        q.width = o - q.x;
        break;
      case "r":
        q.width = o - q.x;
        break;
      case "rt":
        q.width = o - q.x;
        q.height += q.y - H;
        q.y = H;
        break
      }
      B()
    };
    var k = function(r, o, F) {
      _bx = r;
      _by = o
    };
    b([t.node, f.node, n.node, e.node]).bind("click",
    function() {
      if (!a.config.editable) {
        return
      }
      w();
      var o = b(C).data("mod");
      switch (o) {
      case "pointer":
        break;
      case "path":
        var r = b(C).data("currNode");
        if (r && r.getId() != g && r.getId().substring(0, 4) == "rect") {
          b(C).trigger("addpath", [r, u])
        }
        break
      }
      b(C).trigger("click", u);
      b(C).data("currNode", u);
      return false
    });
    var j = function(o, r) {
      if (!a.config.editable) {
        return
      }
      if (r.getId() == g) {
        b(C).trigger("showprops", [E.props, r])
      } else {
        d()
      }
    };
    b(C).bind("click", j);
    var c = function(o, F, r) {
      if (r.getId() == g) {
        f.attr({
          text: F
        })
      }
    };
    b(C).bind("textchange", c);
    function y() {
      return "M" + q.x + " " + q.y + "L" + q.x + " " + (q.y + q.height) + "L" + (q.x + q.width) + " " + (q.y + q.height) + "L" + (q.x + q.width) + " " + q.y + "L" + q.x + " " + q.y
    }
    function w() {
      s.show();
      for (var o in i) {
        i[o].show()
      }
    }
    function d() {
      s.hide();
      for (var o in i) {
        i[o].hide()
      }
    }
    function B() {
      var F = q.x + E.margin,
      r = q.y + E.margin,
      G = q.width - E.margin * 2,
      o = q.height - E.margin * 2;
      t.attr({
        x: F,
        y: r,
        width: G,
        height: o
      });
      switch (E.showType) {
      case "image":
        e.attr({
          x:
          F + (G - E.img.width) / 2,
          y: r + (o - E.img.height) / 2
        }).show();
        break;
      case "text":
        t.show();
        f.attr({
          x:
          F + G / 2,
          y: r + o / 2
        }).show();
        break;
      case "image&text":
        t.show();
        n.attr({
          x:
          F + E.img.width + (G - E.img.width) / 2,
          y: r + a.config.lineHeight / 2
        }).show();
        f.attr({
          x: F + E.img.width + (G - E.img.width) / 2,
          y: r + (o - a.config.lineHeight) / 2 + a.config.lineHeight
        }).show();
        e.attr({
          x: F + E.img.width / 2,
          y: r + (o - E.img.height) / 2
        }).show();
        break
      }
      i.t.attr({
        x: q.x + q.width / 2 - h / 2,
        y: q.y - h / 2
      });
      i.lt.attr({
        x: q.x - h / 2,
        y: q.y - h / 2
      });
      i.l.attr({
        x: q.x - h / 2,
        y: q.y - h / 2 + q.height / 2
      });
      i.lb.attr({
        x: q.x - h / 2,
        y: q.y - h / 2 + q.height
      });
      i.b.attr({
        x: q.x - h / 2 + q.width / 2,
        y: q.y - h / 2 + q.height
      });
      i.rb.attr({
        x: q.x - h / 2 + q.width,
        y: q.y - h / 2 + q.height
      });
      i.r.attr({
        x: q.x - h / 2 + q.width,
        y: q.y - h / 2 + q.height / 2
      });
      i.rt.attr({
        x: q.x - h / 2 + q.width,
        y: q.y - h / 2
      });
      s.attr({
        path: y()
      });
      b(C).trigger("rectresize", u)
    }
    this.toJson = function() {
      var r = "{type:'" + E.type + "',text:{text:'" + f.attr("text") + "'}, attr:{ x:" + Math.round(t.attr("x")) + ", y:" + Math.round(t.attr("y")) + ", width:" + Math.round(t.attr("width")) + ", height:" + Math.round(t.attr("height")) + "}, props:{";
      for (var o in E.props) {
        r += o + ":{value:'" + E.props[o].value + "'},"
      }
      if (r.substring(r.length - 1, r.length) == ",") {
        r = r.substring(0, r.length - 1)
      }
      r += "}}";
      return r
    };
    this.restore = function(o) {    
      var r = o;
      E = b.extend(true, E, o);
      f.attr({
        text: r.text.text
      });
      B()
    }; 
    this.getBBox = function() {
      return q
    };
    this.getId = function() {
      return g
    };
    this.remove = function() {
      t.remove();
      f.remove();
      n.remove();
      e.remove();
      s.remove();
      for (var o in i) {
        i[o].remove()
      }
    };
    this.text = function() {
      return f.attr("text")
    };
    this.attr = function(o) {
      if (o) {
        t.attr(o)
      }
    };
    B()
  };
  a.path = function(q, n, u, e) {
    var v = this,
    z = n,
    B = b.extend(true, {},
    a.config.path),
    i,
    t,
    f,
    h = B.textPos,
    y,
    w,
    k = u,
    s = e,
    g = "path" + a.util.nextId(),
    x;
    function p(G, H, D, L) {
      var F = this,
      M = G,
      r, o = D,
      O = L,
      K, I, N = H;
      switch (M) {
      case "from":
        r = z.rect(H.x - B.attr.fromDot.width / 2, H.y - B.attr.fromDot.height / 2, B.attr.fromDot.width, B.attr.fromDot.height).attr(B.attr.fromDot);
        break;
      case "big":
        r = z.rect(H.x - B.attr.bigDot.width / 2, H.y - B.attr.bigDot.height / 2, B.attr.bigDot.width, B.attr.bigDot.height).attr(B.attr.bigDot);
        break;
      case "small":
        r = z.rect(H.x - B.attr.smallDot.width / 2, H.y - B.attr.smallDot.height / 2, B.attr.smallDot.width, B.attr.smallDot.height).attr(B.attr.smallDot);
        break;
      case "to":
        r = z.rect(H.x - B.attr.toDot.width / 2, H.y - B.attr.toDot.height / 2, B.attr.toDot.width, B.attr.toDot.height).attr(B.attr.toDot);
        break
      }
      if (r && (M == "big" || M == "small")) {
        r.drag(function(Q, P) {
          C(Q, P)
        },
        function() {
          J()
        },
        function() {
          E()
        });
        var C = function(R, Q) {
          var P = (K + R),
          S = (I + Q);
          F.moveTo(P, S)
        };
        var J = function() {
          if (M == "big") {
            K = r.attr("x") + B.attr.bigDot.width / 2;
            I = r.attr("y") + B.attr.bigDot.height / 2
          }
          if (M == "small") {
            K = r.attr("x") + B.attr.smallDot.width / 2;
            I = r.attr("y") + B.attr.smallDot.height / 2
          }
        };
        var E = function() {}
      }
      this.type = function(P) {
        if (P) {
          M = P
        } else {
          return M
        }
      };
      this.node = function(P) {
        if (P) {
          r = P
        } else {
          return r
        }
      };
      this.left = function(P) {
        if (P) {
          o = P
        } else {
          return o
        }
      };
      this.right = function(P) {
        if (P) {
          O = P
        } else {
          return O
        }
      };
      this.remove = function() {
        o = null;
        O = null;
        r.remove()
      };
      this.pos = function(P) {
        if (P) {
          N = P;
          r.attr({
            x: N.x - r.attr("width") / 2,
            y: N.y - r.attr("height") / 2
          });
          return this
        } else {
          return N
        }
      };
      this.moveTo = function(Q, T) {
        this.pos({
          x: Q,
          y: T
        });
        switch (M) {
        case "from":
          if (O && O.right() && O.right().type() == "to") {
            O.right().pos(a.util.connPoint(s.getBBox(), N))
          }
          if (O && O.right()) {
            O.pos(a.util.center(N, O.right().pos()))
          }
          break;
        case "big":
          if (O && O.right() && O.right().type() == "to") {
            O.right().pos(a.util.connPoint(s.getBBox(), N))
          }
          if (o && o.left() && o.left().type() == "from") {
            o.left().pos(a.util.connPoint(k.getBBox(), N))
          }
          if (O && O.right()) {
            O.pos(a.util.center(N, O.right().pos()))
          }
          if (o && o.left()) {
            o.pos(a.util.center(N, o.left().pos()))
          }
          var S = {
            x: N.x,
            y: N.y
          };
          if (a.util.isLine(o.left().pos(), S, O.right().pos())) {
            M = "small";
            r.attr(B.attr.smallDot);
            this.pos(S);
            var P = o;
            o.left().right(o.right());
            o = o.left();
            P.remove();
            var R = O;
            O.right().left(O.left());
            O = O.right();
            R.remove()
          }
          break;
        case "small":
          if (o && O && !a.util.isLine(o.pos(), {
            x: N.x,
            y: N.y
          },
          O.pos())) {
            M = "big";
            r.attr(B.attr.bigDot);
            var P = new p("small", a.util.center(o.pos(), N), o, o.right());
            o.right(P);
            o = P;
            var R = new p("small", a.util.center(O.pos(), N), O.left(), O);
            O.left(R);
            O = R
          }
          break;
        case "to":
          if (o && o.left() && o.left().type() == "from") {
            o.left().pos(a.util.connPoint(k.getBBox(), N))
          }
          if (o && o.left()) {
            o.pos(a.util.center(N, o.left().pos()))
          }
          break
        }
        m()
      }
    }
    function j() {
      var D, C, E = k.getBBox(),
      F = s.getBBox(),
      r,
      o;
      r = a.util.connPoint(E, {
        x: F.x + F.width / 2,
        y: F.y + F.height / 2
      });
      o = a.util.connPoint(F, r);
      D = new p("from", r, null, new p("small", {
        x: (r.x + o.x) / 2,
        y: (r.y + o.y) / 2
      }));
      D.right().left(D);
      C = new p("to", o, D.right(), null);
      D.right().right(C);
      this.toPathString = function() {
        if (!D) {
          return ""
        }
        var J = D,
        I = "M" + J.pos().x + " " + J.pos().y,
        H = "";
        while (J.right()) {
          J = J.right();
          I += "L" + J.pos().x + " " + J.pos().y
        }
        var G = a.util.arrow(J.left().pos(), J.pos(), B.attr.arrow.radius);
        H = "M" + G[0].x + " " + G[0].y + "L" + G[1].x + " " + G[1].y + "L" + G[2].x + " " + G[2].y + "z";
        return [I, H]
      };
      this.toJson = function() {
        var G = "[",
        H = D;
        while (H) {
          if (H.type() == "big") {
            G += "{x:" + Math.round(H.pos().x) + ",y:" + Math.round(H.pos().y) + "},"
          }
          H = H.right()
        }
        if (G.substring(G.length - 1, G.length) == ",") {
          G = G.substring(0, G.length - 1)
        }
        G += "]";
        return G
      };
      this.restore = function(H) {
        var I = H,
        J = D.right();
        for (var G = 0; G < I.length; G++) {
          J.moveTo(I[G].x, I[G].y);
          J.moveTo(I[G].x, I[G].y);
          J = J.right()
        }
        this.hide()
      };
      this.fromDot = function() {
        return D
      };
      this.toDot = function() {
        return C
      };
      this.midDot = function() {
        var H = D.right(),
        G = D.right().right();
        while (G.right() && G.right().right()) {
          G = G.right().right();
          H = H.right()
        }
        return H
      };
      this.show = function() {
        var G = D;
        while (G) {
          G.node().show();
          G = G.right()
        }
      };
      this.hide = function() {
        var G = D;
        while (G) {
          G.node().hide();
          G = G.right()
        }
      };
      this.remove = function() {
        var G = D;
        while (G) {
          if (G.right()) {
            G = G.right();
            G.left().remove()
          } else {
            G.remove();
            G = null
          }
        }
      }
    }
    B = b.extend(true, B, q);
    i = z.path(B.attr.path.path).attr(B.attr.path);
    t = z.path(B.attr.arrow.path).attr(B.attr.arrow);
    x = new j();
    x.hide();
    f = z.text(0, 0, B.text.text).attr(B.text).attr({
      text: B.text.text.replace("{from}", k.text()).replace("{to}", s.text())
    });
    f.drag(function(r, o) {
      if (!a.config.editable) {
        return
      }
      f.attr({
        x: y + r,
        y: w + o
      })
    },
    function() {
      y = f.attr("x");
      w = f.attr("y")
    },
    function() {
      var o = x.midDot().pos();
      h = {
        x: f.attr("x") - o.x,
        y: f.attr("y") - o.y
      }
    });
    m();
    b([i.node, t.node]).bind("click",
    function() {
      if (!a.config.editable) {
        return
      }
      b(z).trigger("click", v);
      b(z).data("currNode", v);
      return false
    });
    var l = function(r, C) {
      if (!a.config.editable) {
        return
      }
      if (C && C.getId() == g) {
        x.show();
        b(z).trigger("showprops", [B.props, v])
      } else {
        x.hide()
      }
      var o = b(z).data("mod");
      switch (o) {
      case "pointer":
        break;
      case "path":
        break
      }
    };
    b(z).bind("click", l);
    var A = function(o, r) {
      if (!a.config.editable) {
        return
      }
      if (r && (r.getId() == k.getId() || r.getId() == s.getId())) {
        b(z).trigger("removepath", v)
      }
    };
    b(z).bind("removerect", A);
    var d = function(C, D) {
      if (!a.config.editable) {
        return
      }
      if (k && k.getId() == D.getId()) {
        var o;
        if (x.fromDot().right().right().type() == "to") {
          o = {
            x: s.getBBox().x + s.getBBox().width / 2,
            y: s.getBBox().y + s.getBBox().height / 2
          }
        } else {
          o = x.fromDot().right().right().pos()
        }
        var r = a.util.connPoint(k.getBBox(), o);
        x.fromDot().moveTo(r.x, r.y);
        m()
      }
      if (s && s.getId() == D.getId()) {
        var o;
        if (x.toDot().left().left().type() == "from") {
          o = {
            x: k.getBBox().x + k.getBBox().width / 2,
            y: k.getBBox().y + k.getBBox().height / 2
          }
        } else {
          o = x.toDot().left().left().pos()
        }
        var r = a.util.connPoint(s.getBBox(), o);
        x.toDot().moveTo(r.x, r.y);
        m()
      }
    };
    b(z).bind("rectresize", d);
    var c = function(r, o, C) {
      if (C.getId() == g) {
        f.attr({
          text: o
        })
      }
    };
    b(z).bind("textchange", c);
    this.from = function() {
      return k
    };
    this.to = function() {
      return s
    };
    this.toJson = function() {
      var r = "{from:'" + k.getId()
          + "',to:'" + s.getId()
          + "', dots:" + x.toJson()
          + ",text:{text:'" + f.attr("text")
          + "'},textPos:{x:" + Math.round(h.x) + ",y:" + Math.round(h.y)
          + "}, props:{";
      for (var o in B.props) {
        r += o + ":{value:'" + B.props[o].value + "'},"
      }
      if (r.substring(r.length - 1, r.length) == ",") {
        r = r.substring(0, r.length - 1)
      }
      r += "}}";
      return r
    };
    this.restore = function(o) {
      var r = o;
      B = b.extend(true, B, o);
      x.restore(r.dots)
    };
    this.remove = function() {
      x.remove();
      i.remove();
      t.remove();
      f.remove();
      try {
        b(z).unbind("click", l)
      } catch(o) {}
      try {
        b(z).unbind("removerect", A)
      } catch(o) {}
      try {
        b(z).unbind("rectresize", d)
      } catch(o) {}
      try {
        b(z).unbind("textchange", c)
      } catch(o) {}
    };
    function m() {
      var r = x.toPathString(),
      o = x.midDot().pos();
      i.attr({
        path: r[0]
      });
      t.attr({
        path: r[1]
      });
      f.attr({
        x: o.x + h.x,
        y: o.y + h.y
      })
    }
    this.getId = function() {
      return g
    };
    this.text = function() {
      return f.attr("text")
    };
    this.attr = function(o) {
      if (o && o.path) {
        i.attr(o.path)
      }
      if (o && o.arrow) {
        t.attr(o.arrow)
      }
    }
  };
  a.props = function(h, f) {
    var j = this,
    c = b("#myflow_props").hide().draggable({
      handle: "#myflow_props_handle"
    }).resizable().css(a.config.props.attr).bind("click",
    function() {
      return false
    }),
    e = c.find("table"),
    g = f,
    i;
    var d = function(n, m, o) {
      if (i && i.getId() == o.getId()) {
        return
      }
      i = o;
      b(e).find(".editor").each(function() {
        var k = b(this).data("editor");
        if (k) {
          k.destroy()
        }
      });
      e.empty();
      c.show();
      for (var l in m) {
        e.append("<tr><th>" + m[l].label + '</th><td><div id="p' + l + '" class="editor"></div></td></tr>');
        if (m[l].editor) {
          m[l].editor().init(m, l, "p" + l, o, g)
        }
      }
    };
    b(g).bind("showprops", d)
  };
  a.editors = {
    textEditor: function() {
      var d, e, c, g, f;
      this.init = function(i, h, m, l, j) {
        d = i;
        e = h;
        c = m;
        g = l;
        f = j;
        b('<input  style="width:100%;"/>').val(g.text()).change(function() {
          i[e].value = b(this).val();
          b(f).trigger("textchange", [b(this).val(), g])
        }).appendTo("#" + c);
        b("#" + c).data("editor", this)
      };
      this.destroy = function() {
        b("#" + c + " input").each(function() {
          d[e].value = b(this).val();
          b(f).trigger("textchange", [b(this).val(), g])
        })
      }
    }
  };
  a.init = function(x, r) {
    var v = b(window).width(),
    e = b(window).height(),
    y = Raphael(x, v * 1.5, e * 1.5),
    q = {},
    g = {};
    b.extend(true, a.config, r);
    b(document).keydown(function(i) {
      if (!a.config.editable) {
        return
      }
      if (i.keyCode == 46) {
        var j = b(y).data("currNode");
        if (j) {
          if (j.getId().substring(0, 4) == "rect") {
            b(y).trigger("removerect", j)
          } else {
            if (j.getId().substring(0, 4) == "path") {
              b(y).trigger("removepath", j)
            }
          }
          b(y).removeData("currNode")
        }
      }
    });
    b(document).click(function() {
      b(y).data("currNode", null);
      b(y).trigger("click", {
        getId: function() {
          return "00000000"
        }
      });
      b(y).trigger("showprops", [a.config.props.props, {
        getId: function() {
          return "00000000"
        }
      }])
    });
    var w = function(c, i) {
      if (!a.config.editable) {
        return
      }
      if (i.getId().substring(0, 4) == "rect") {
        q[i.getId()] = null;
        i.remove()
      } else {
        if (i.getId().substring(0, 4) == "path") {
          g[i.getId()] = null;
          i.remove()
        }
      }
    };
    b(y).bind("removepath", w);
    b(y).bind("removerect", w);
    b(y).bind("addrect",
    function(j, c, k) {
      var i = new a.rect(b.extend(true, {},
      a.config.tools.states[c], k), y);
      q[i.getId()] = i
    });
    var f = function(i, k, j) {
      var c = new a.path({},
      y, k, j);
      g[c.getId()] = c
    };
    b(y).bind("addpath", f);
    b(y).data("mod", "point");
    if (a.config.editable) {
      b("#myflow_tools").draggable({
        handle: "#myflow_tools_handle"
      }).css(a.config.tools.attr);
      b("#myflow_tools .node").hover(function() {
        b(this).addClass("mover")
      },
      function() {
        b(this).removeClass("mover")
      });
      b("#myflow_tools .selectable").click(function() {
        b(".selected").removeClass("selected");
        b(this).addClass("selected");
        b(y).data("mod", this.id)
      });
      b("#myflow_tools .state").each(function() {
        b(this).draggable({
          helper: "clone"
        })
      });
      b(x).droppable({
        accept: ".state",
        drop: function(c, i) {
          b(y).trigger("addrect", [i.helper.attr("type"), {
            attr: {
              x: i.helper.offset().left,
              y: i.helper.offset().top
            }
          }])
        }
      });
      b("#myflow_save").click(function() {
        var i = "{states:{";
        for (var c in q) {
          if (q[c]) {
            i += q[c].getId() + ":" + q[c].toJson() + ","
          }
        }
        if (i.substring(i.length - 1, i.length) == ",") {
          i = i.substring(0, i.length - 1)
        }
        i += "},paths:{";
        for (var c in g) {
          if (g[c]) {
            i += g[c].getId() + ":" + g[c].toJson() + ","
          }
        }
        if (i.substring(i.length - 1, i.length) == ",") {
          i = i.substring(0, i.length - 1)
        }
        i += "},props:{props:{";
        for (var c in a.config.props.props) {
          i += c + ":{value:'" + a.config.props.props[c].value + "'},"
        }
        if (i.substring(i.length - 1, i.length) == ",") {
          i = i.substring(0, i.length - 1)
        }
        i += "}}}";
        a.config.tools.save.onclick(i)
      });
      new a.props({},
      y)
    }
    if (r.restore) {
      var B = r.restore;
      var z = {};
      if (B.states) {
        for (var s in B.states) {
          var d = new a.rect(b.extend(true, {},
          a.config.tools.states[B.states[s].type], B.states[s]), y);
          d.restore(B.states[s]);
          z[s] = d;
          q[d.getId()] = d
        }
      }
      if (B.paths) {
        for (var s in B.paths) {
          var n = new a.path(b.extend(true, {},
          a.config.tools.path, B.paths[s]), y, z[B.paths[s].from], z[B.paths[s].to]);
          n.restore(B.paths[s]);
          g[n.getId()] = n
        }
      }
    }
    var A = a.config.historyRects,
    l = a.config.activeRects;
    if (A.rects.length || l.rects.length) {
      var m = {},
      z = {};
      for (var h in g) {
        if (!z[g[h].from().text()]) {
          z[g[h].from().text()] = {
            rect: g[h].from(),
            paths: {}
          }
        }
        z[g[h].from().text()].paths[g[h].text()] = g[h];
        if (!z[g[h].to().text()]) {
          z[g[h].to().text()] = {
            rect: g[h].to(),
            paths: {}
          }
        }
      }
      for (var u = 0; u < A.rects.length; u++) {
        if (z[A.rects[u].name]) {
          z[A.rects[u].name].rect.attr(A.rectAttr)
        }
        for (var t = 0; t < A.rects[u].paths.length; t++) {
          if (z[A.rects[u].name].paths[A.rects[u].paths[t]]) {
            z[A.rects[u].name].paths[A.rects[u].paths[t]].attr(A.pathAttr)
          }
        }
      }
      for (var u = 0; u < l.rects.length; u++) {
        if (z[l.rects[u].name]) {
          z[l.rects[u].name].rect.attr(l.rectAttr)
        }
        for (var t = 0; t < l.rects[u].paths.length; t++) {
          if (z[l.rects[u].name].paths[l.rects[u].paths[t]]) {
            z[l.rects[u].name].paths[l.rects[u].paths[t]].attr(l.pathAttr)
          }
        }
      }
    }
  };
  b.fn.myflow = function(c) {
    return this.each(function() {
      a.init(this, c)
    })
  };
  b.myflow = a
})(jQuery);